package db;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import beans.UserBean;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletContext;

/**
 * Class which works with user.
 * You can add, edit and delete user.
 * @author Tomas CASTA
 *
 */
public class User {
    /** user*/
    private UserBean userBean;
    /** connection to db */
    private Connection connection;
    /** message for errors */
    private String msg;
    /** servlet context */
    private ServletContext context;
    /** list of users  */
    private ArrayList<UserBean> list;
     /** statement for queries */
    private PreparedStatement stmt;
    /** set for results */
    private ResultSet resultSet;
    /** another message for errors */
    private String errMsg;
    /** default user id, will be set as message author, when author is deleted */
    private static final int DEFAULT_USER = 1;

    /**
     * Copy from http://www.java-examples.com
     * Get md5 hash from input.
     * @param input String input
     * @return String result
     */
    private String md5(String input) {
        String result = input;
        if (input != null) {
            try {
                MessageDigest md = MessageDigest.getInstance("MD5"); //or "SHA-1"
                md.update(input.getBytes());
                BigInteger hash = new BigInteger(1, md.digest());
                result = hash.toString(16);
                while (result.length() < 32) {
                    result = "0" + result;
                }
            } catch (NoSuchAlgorithmException ex) {
                Logger.getLogger(User.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return result;
    }

    public void setErrMsg(String errMsg) {
        this.errMsg = errMsg;
    }

    public String getErrMsg() {
        return errMsg;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public UserBean getUserBean() {
        return userBean;
    }

    public void setUserBean(UserBean userBean) {
        this.userBean = userBean;
    }

    public ArrayList<UserBean> getList() {
        return list;
    }

    public void setList(ArrayList<UserBean> list) {
        this.list = list;
    }

    /**
     * Constructor.
     * @param login String user login
     * @param password String user password
     * @param email String user email
     * @param info String user info
     * @param gender String user gender
     * @param dateOfBirth Date user date of birth
     */
    public User(String login, String password, String email, String info, String gender, Date dateOfBirth) {
        this.userBean = new UserBean(-1, login, password, email, info, gender, dateOfBirth, UserBean.USER);
        this.list = new ArrayList<UserBean>();

    }

    /**
     * Constructor.
     */
    public User() {
        this.list = new ArrayList<UserBean>();
    }


    /**
     * Set connection to db.
     * @param context ServletContext context of actual servler
     * @return boolean true ok, false error
     */
    public boolean setDb(ServletContext context) {
        Db db = new Db();

        /** Get params from web.xml */
        db.setDb(context.getInitParameter("db-machine"), context.getInitParameter("db-db"), context.getInitParameter("db-user"), context.getInitParameter("db-pass"));
        this.connection = db.connectToDB();

        msg = db.getMsg();
        if (this.connection == null) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * Delete user by given id.
     * @param id int id user to delete
     * @return boolean true ok, false error
     */
    public boolean deleteUser(int id) {
        try {
            stmt = this.connection.prepareStatement("DELETE FROM casta_user WHERE id = ?");
            stmt.setInt(1, id);

            /* Error in query */
            if (stmt.executeUpdate() != 1) {
                stmt.close();
                connection.close();
                return false;
            }

            /* We will not delete user´s messages, set it to id_user 1 (user with name user deleted) */
            stmt = this.connection.prepareStatement("UPDATE casta_message SET id_user = ? WHERE id_user = ?");
            stmt.setInt(1, DEFAULT_USER);
            stmt.setInt(2, id);

            /* Error in query */
            if (stmt.executeUpdate() != 1) {
                stmt.close();
                connection.close();
                return false;
            }

            stmt.close();
            connection.close();
            return true;

        } catch (SQLException ex) {
            Logger.getLogger(Forum.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }


    }

    /**
     * Find user by given id.
     * @param id int id user for searching
     * @return boolean true ok, false error
     */
    public boolean findById(int id) {

        try {

            /* Find user*/
            stmt = connection.prepareStatement("SELECT * FROM casta_user WHERE id = ?");
            stmt.setInt(1, id);
            ResultSet rs = stmt.executeQuery();

            /* Save info from db to userBean */
            if (rs.next()) {
                this.userBean = new UserBean(id, rs.getString("login"), rs.getString("password"), rs.getString("email"),
                        rs.getString("info"), rs.getString("gender"), rs.getDate("date_of_birth"), rs.getInt("admin"));
                stmt.close();
                connection.close();
                return true;
            } else {
                return false;
            }

        } catch (SQLException ex) {
            Logger.getLogger(User.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }
        catch (NullPointerException ex) {
            Logger.getLogger(User.class.getName()).log(Level.SEVERE, null, ex);
            errMsg = "Databáze není přístupná.";
            return false;
        }


    }

    /**
     * Login user by fiven login and password.
     * @param login String login name
     * @param password String password
     * @return boolean true ok, false error
     */
    public boolean loginUser(String login, String password) {
        try {
            String passwordHash = null;
            stmt = connection.prepareStatement("SELECT * FROM casta_user WHERE login = ?");
            stmt.setString(1, login);
            ResultSet rs = stmt.executeQuery();
            
            /* Get password hash */
            if (rs.next()) {
                passwordHash = this.md5(password);
            }

            Date date = null;

            /* Test if passwords are same */
            if (passwordHash.compareTo(rs.getString("password")) == 0) {

                /* Date in db can be null, test it*/
                if (rs.getDate("date_of_birth") != null) {
                    date = new Date();
                    date.setTime(rs.getDate("date_of_birth").getTime());
                } else {
                    date = new Date();
                }
                userBean = new UserBean(rs.getInt("id"), rs.getString("login"), rs.getString("password"), rs.getString("email"), rs.getString("info"), rs.getString("gender"), date, rs.getInt("admin"));

                return true;
            }
            /* Passswords are not same */
            else {
                errMsg = "Login nebo heslo není správné.";
                return false;
            }

        } catch (SQLException ex) {
            Logger.getLogger(User.class.getName()).log(Level.SEVERE, null, ex);
            errMsg = "Login nebo heslo není správné.";
            return false;
        }
        catch (NullPointerException ex) {
            Logger.getLogger(User.class.getName()).log(Level.SEVERE, null, ex);
            errMsg = "Databáze není přístupná.";
            return false;
        }
    }


    /**
     * Show all forum users.
     * @return boolean true ok, false error
     */
    public boolean showAllUsers() {
        try {

            /* Select all users */
            stmt = connection.prepareStatement("SELECT * FROM casta_user");
            ResultSet rs = stmt.executeQuery();

            /* Add all users to list */
            while (rs.next()) {
                this.list.add(new UserBean(rs.getInt("id"), rs.getString("login"), rs.getString("password"), rs.getString("email"),
                        rs.getString("info"), rs.getString("gender"), rs.getDate("date_of_birth"), rs.getInt("admin")));

            }
            stmt.close();
            connection.close();
            return true;

        } catch (SQLException ex) {
            Logger.getLogger(User.class.getName()).log(Level.SEVERE, null, ex);

            return false;
        }
    }


    /**
     * Test if user login is used.
     * @param login String user login
     * @return boolean true is used, false is unused
     */
    public boolean isLoginUsed(String login) {
        try {
            /* Test if login is unused */
            stmt = connection.prepareStatement("SELECT id FROM casta_user WHERE login = ?");
            stmt.setString(1, userBean.getLogin());
            ResultSet rs = stmt.executeQuery();

            /* User already exists */
            if (rs.next()) {
                stmt.close();
                connection.close();
                return true;
            }
            /* Everything is OK */
            stmt.close();
            connection.close();
            return false;

        } catch (SQLException ex) {
            Logger.getLogger(User.class.getName()).log(Level.SEVERE, null, ex);

            return false;
        }
        catch (NullPointerException ex) {
            Logger.getLogger(User.class.getName()).log(Level.SEVERE, null, ex);
            errMsg = "Databáze není přístupná.";
            return false;
        }
    }

    /**
     * Add new user to db.
     * @return true  ok, false error
     */
    public int addUser() {
        try {

            /* Test if login is unused */
            stmt = connection.prepareStatement("SELECT id FROM casta_user WHERE login = ?");
            stmt.setString(1, userBean.getLogin());
            ResultSet rs = stmt.executeQuery();

            /* User already exists */
            if (rs.next()) {
                stmt.close();
                connection.close();
                msg = "Uživatel s loginem " + userBean.getLogin() + " již existuje";
                return -1;
            }

            /* Get nd5 hash of password */
            String md5 = this.md5(userBean.getPassword());

            /* Login is unused */
            stmt = null;
            stmt = connection.prepareStatement("INSERT INTO casta_user (id, login, password, email, info, gender, date_of_birth, admin) VALUES (NULL, ?, ?, ?, ?, ?, ?, ?)");
            stmt.setString(1, userBean.getLogin());
            stmt.setString(2, md5);
            stmt.setString(3, userBean.getEmail());
            stmt.setString(4, userBean.getInfo());
            stmt.setString(5, userBean.getGender());

            /* Date of birth is not null, convert it for db date*/
            if (userBean.getDateOfBirth() != null) {
                stmt.setDate(6, new java.sql.Date(userBean.getDateOfBirth().getTime()));
            }
            /* Date is null, set as null to db */
            else {
                stmt.setNull(6, java.sql.Types.DATE);
            }
            stmt.setInt(7, userBean.getAdmin());

            /* Error in query */
            if (stmt.executeUpdate() != 1) {
                stmt.close();
                connection.close();
                return -2;
            }

            /* Everything is OK */
            stmt.close();
            connection.close();
            return 1;

        } /* Error */ catch (Exception e) {
            errMsg = "Databáze není přístupná.";
            Logger.getLogger(User.class.getName()).log(Level.SEVERE, null, e);
            return -3;
        }

    }


    /**
     * Get user age from date of birth.
     * @return age int user age, -1 error 
     */
    public int getAge() {

        /* Get calendars*/
        Calendar today = Calendar.getInstance();
        Calendar birth = Calendar.getInstance();
        int age;
        if (userBean.getDateOfBirth() != null) {
            birth.setTime(userBean.getDateOfBirth());

            /* Age is current year minus birth year */
            age = today.get(Calendar.YEAR) - birth.get(Calendar.YEAR);
            birth.add(Calendar.YEAR, age);

            /* In this year user have not birthday, decrease age */
            if (today.before(birth)) {
                age--;
            }
        } else {
            age = -1;
        }
        return age;
    }


}
